﻿/*********************************
 $Author: qinjunIt@gmail.com $
 $Modtime: 09-04-21 9:54 $
 $Revision: 43 $
********************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Common;
using System.IO;
using System.Reflection;
using System.Web;
using Vbyte.SharpOrm.Config;
using Vbyte.SharpOrm.SqlDb;
using Vbyte.DataSource.Cache;


namespace Vbyte.SharpOrm
{
    /// <summary>
    /// <para>为SQL映射绑定所做的辅助操作</para>
    /// <para> 1.0.3.0 2009-4-18   增加新增、删除、更新的事务支持。</para>
    /// <para> 1.0.2.1 2009-4-13   完善实例类型（字段设置）的条件约束，增加字符串类型数据参数的字节长度设置。</para>
    /// <para> 1.0.1.2 2009-3-18   BugFix:修复多表关联默认值没有做为参数条件传递，完善别名前缀。</para>
    /// <para> 1.0.1.1 2009-3-6    增加获取映射实例的数据表格方法(+1重载)、获取单列的数据数组</para>
    /// <para> 1.0.1.0 2009-3-5    增加多表关联的数据获取命令文本(Select)</para>
    /// <para> 1.0.0.5 2009-2-28   BugFix:修复获取统计数量时出现的OrderBy参数</para>
    /// <para> 1.0.0.4 2009-2-27   BugFix:修复使用共享打开数据库连接的逻辑错误</para>
    /// <para> 1.0.0.3 2009-2-26   增加更新强制不忽略默认值重载</para>
    /// <para> 1.0.0.2 2009-2-25   增加简易数据列表的自定义过滤器</para>
    /// <para> 1.0.0.1 2009-2-25   增加获取列表的总数统计</para>
    /// </summary>
    /// <remarks>
    /// 映射的主要凭据为相关属性的默认值，也可强制不忽略相关属性的默认值。
    /// */
    /// </remarks>
    public  static partial class OrmHelper
    {
        /// <summary>
        /// 输出信息到调试文件/Logs/debug.log
        /// </summary>
        /// <param name="obj"></param>
        public static void Debug(object obj)
        {
            #if DEBUG
            string debugDir = "/logs";
            if (HttpContext.Current != null)
            {
                debugDir = HttpContext.Current.Server.MapPath(debugDir);
            }
            else
            {
                debugDir = AppDomain.CurrentDomain.BaseDirectory + debugDir;
            }
            if (!Directory.Exists(debugDir)) Directory.CreateDirectory(debugDir);
            using (StreamWriter log = new StreamWriter(debugDir + "\\SharpOrm.log",
                true))
            {
                if (obj != null && obj.GetType().IsSubclassOf(typeof(DbCommand)))
                {
                    DbCommand cmd = obj as DbCommand;
                    log.WriteLine(cmd.CommandText);

                    foreach (DbParameter p in cmd.Parameters)
                    {
                        log.WriteLine("@" + p.ParameterName + " = " + p.Value);
                    }

                }
                else
                {
                    log.WriteLine(obj);
                }
                log.Close();
            }
            #endif
        }

        #region 功能辅助代码
        /// <summary>
        /// 判定值类型是否是其默认值
        /// </summary>
        /// <param name="valueTypeName">值类型的CLR类型</param>
        /// <param name="instanceValue">该类型的一个实例</param>
        /// <returns></returns>
        internal static bool ValueTypeIsDefaultValue(Type valueTypeName, object instanceValue)
        {
            /*
             默认值表（C# 参考）
             http://msdn.microsoft.com/zh-cn/library/83fhsxwc(VS.80).aspx
             bool false    byte 0    char '\0'
             decimal 0.0M  double 0.0D
             enum 表达式 (E)0 产生的值，其中 E 为 enum 标识符。
             float 0.0F   int 0  long 0L
             sbyte 0 short 0
             struct 将所有的值类型字段设置为默认值并将所有的引用类型字段设置为 null 时产生的值。
             uint 0 ulong 0 ushort 0
             */
            bool result = false;
            if (valueTypeName == typeof(bool))
            {
                result = (Convert.ToBoolean(instanceValue) == false);
            }
            else if (valueTypeName == typeof(char))
            {
                result = ((char)instanceValue == '\0');
            }
            else if (valueTypeName == typeof(DateTime))
            {
                result = Convert.ToDateTime(instanceValue).Equals(default(DateTime));
            }
            else if (valueTypeName.IsGenericType
                && valueTypeName.GetGenericTypeDefinition().Equals(typeof(Nullable<>).GetGenericTypeDefinition()))
            {
                return instanceValue == null;
            }
            else
            {
                result = (instanceValue.ToString() == "0");
            }
            return result;
        }

        /// <summary>
        /// 判定引用类型是否是其默认Null值。
        /// </summary>
        /// <param name="valueTypeName">值类型的CLR类型</param>
        /// <param name="instanceValue">该类型的一个实例</param>
        /// <returns></returns>
        internal static bool RefTypeIsDefaultValue(Type valueTypeName, object instanceValue)
        {
            return instanceValue == null;
        }

        /// <summary>
        /// 判定指定类型的实例是否是其默认值
        /// </summary>
        /// <param name="valueTypeName">值类型的CLR类型</param>
        /// <param name="instanceValue">该类型的一个实例</param>
        /// <returns>
        /// 	<c>true</c> if [is instance bind default value] [the specified value type name]; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsInstanceBindDefaultValue(Type valueTypeName, object instanceValue)
        {
            if (valueTypeName.IsValueType)
            {
                return ValueTypeIsDefaultValue(valueTypeName, instanceValue);
            }
            else
            {
                return RefTypeIsDefaultValue(valueTypeName, instanceValue);
            }
        }

        /// <summary>
        /// 判定某成员信息是否包含特定属性的配置
        /// </summary>
        /// <param name="mInfo">成员属性实例</param>
        /// <param name="AttrType">自定义属性类型</param>
        /// <param name="inherit">是否从集成对象中查找</param>
        /// <returns>是否存在特定属性的配置</returns>
        public static bool HasAttribute(MemberInfo mInfo, Type AttrType, bool inherit)
        {
            object[] attrs = mInfo.GetCustomAttributes(AttrType, inherit);
            return (attrs != null && attrs.Length > 0);
        }

        /// <summary>
        /// 判定某实例类型是否有指定属性的配置
        /// </summary>
        /// <param name="instanceType">实例类型</param>
        /// <param name="AttrType">自定义属性类型</param>
        /// <param name="inherit">是否从集成对象中查找</param>
        /// <returns>是否存在特定属性的配置</returns>
        public static bool HasAttribute(Type instanceType, Type AttrType, bool inherit)
        {
            object[] attrs = instanceType.GetCustomAttributes(AttrType, inherit);
            return (attrs != null && attrs.Length > 0);
        }

        /// <summary>
        /// 获取某成员信息的特定属性的配置
        /// </summary>
        /// <typeparam name="T">自定义属性类型</typeparam>
        /// <param name="mInfo">成员属性实例</param>
        /// <param name="inherit">是否从集成对象中查找</param>
        /// <returns>如果不存在则放回空数组</returns>
        public static T[] GetMemberInfoCustomAttributes<T>(MemberInfo mInfo, bool inherit)
        {
            object[] attrs = mInfo.GetCustomAttributes(typeof(T), inherit);
            if (attrs != null && attrs.Length > 0)
            {
                return Array.ConvertAll<object, T>(attrs, new Converter<object, T>(delegate(object o)
                    {
                        return (T)o;
                    }));
            }
            else
            {
                return new T[0];
            }
        }

        /// <summary>
        /// 获取某实例类型的指定属性的配置
        /// </summary>
        /// <typeparam name="T">自定义属性类型</typeparam>
        /// <param name="instanceType">实例类型</param>
        /// <param name="inherit">是否从集成对象中查找</param>
        /// <returns>如果不存在则放回空数组</returns>
        public static T[] GetInstanceCustomAttributes<T>(Type instanceType, bool inherit)
        {
            object[] attrs = instanceType.GetCustomAttributes(typeof(T), inherit);
            if (attrs != null && attrs.Length > 0)
            {
                return Array.ConvertAll<object, T>(attrs, new Converter<object, T>(delegate(object o)
                {
                    return (T)o;
                }));
            }
            else
            {
                return new T[0];
            }
        } 

        internal static bool StringArrayContains(string[] strArray, string str)
        {
            if (strArray == null || strArray.Length == 0) return false;

            string foundList = "," + string.Join(",", strArray) + ",";
            return foundList.IndexOf("," + str + ",", StringComparison.InvariantCultureIgnoreCase) != -1;
        }

        /// <summary>
        /// 获取实例公开属性的非默认值集合
        /// </summary>
        /// <param name="t">实例类型</param>
        /// <param name="instance">该实例</param>
        /// <returns></returns>
        internal static string[] GetInstanceBind(Type t, object instance)
        {
            List<string> propList = new List<string>();
            string propName = "";
            object propValue = null;
            PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(t.FullName, () => t.GetProperties());
            foreach (PropertyInfo pi in Properties)
            {
                propName = pi.Name;
                propValue = pi.GetGetMethod().Invoke(instance, null);
                bool blnCurrentBindDefault = IsInstanceBindDefaultValue(pi.PropertyType, propValue);
                if (blnCurrentBindDefault == false)
                {
                    propList.Add(propName);
                }
            }
            return propList.ToArray();
        }

        /// <summary>
        /// 获取实例的属性绑定值
        /// </summary>
        /// <param name="t">实例类型</param>
        /// <param name="instance">该实例</param>
        /// <param name="propName">公共属性名称</param>
        /// <returns>该实例属性的值</returns>
        internal static object GetInstanceProperty(Type t, object instance, string propName)
        {
            object ret = null;
            PropertyInfo pi = t.GetProperty(propName);
            ret = pi.GetGetMethod().Invoke(instance, null);
            return ret;
        }

        /// <summary>
        /// 判定是否是已设置绑定的实例类型
        /// </summary>
        /// <param name="sqlTableEntry">表对象映射实例，该对象原型有<see cref="BindTableAttribute"/>特性标记。</param>
        /// <returns>
        /// 	<c>true</c> 假设已设置绑定信息; 否则为 <c>false</c>.
        /// </returns>
        internal static bool IsBindTableEntry(
            #if NETFX35
            this 
            #endif
            TableEntry sqlTableEntry)
        {
            BindTableAttribute[] bdTabAttrs = GetInstanceCustomAttributes<BindTableAttribute>(sqlTableEntry.GetType(), true);
            return bdTabAttrs.Length > 0;
        }

        /// <summary>
        /// 判定映射实例是否已填充数据(任意主键为默认值)
        /// </summary>
        /// <param name="sqlTableEntry">表映射实例</param>
        /// <returns>
        /// 	<c>true</c> if [is bind entry is filled] [the specified SQL table entry]; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsBindEntryIsFilled(
            #if NETFX35
            this 
            #endif
            TableEntry sqlTableEntry)
        {
            if (!IsBindTableEntry(sqlTableEntry))
            {
                throw new InvalidOperationException("指定对象没有配置BindTableAttribute属性！");
            }

            Type t = sqlTableEntry.GetType();
            object propValue = null;
            bool blnResult = true;
            PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(t.FullName, () => t.GetProperties());
            foreach (PropertyInfo pi in Properties)
            {
                propValue = pi.GetGetMethod().Invoke(sqlTableEntry, null);
                bool blnCurrentBindDefault = IsInstanceBindDefaultValue(pi.PropertyType, propValue);
                bool isPrimaryKey = HasAttribute(pi, typeof(PrimaryKeyAttribute), true);
                if (isPrimaryKey && blnCurrentBindDefault)
                {
                    blnResult = false;
                    break;
                }

            }
            return blnResult;
        }
        #endregion

        #region 其他扩展

        /// <summary>
        /// 绑定指定特性标记的属性值
        /// </summary>
        /// <param name="instance">绑定实例</param>
        /// <param name="attrType">特性类型</param>
        /// <param name="propValue">属性的值</param>
        public static void BindByAttribute(this object instance, Type attrType, object propValue)
        {
            if (instance == null) return;
            Type instanceType = instance.GetType();
            PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(instanceType.FullName, () => instanceType.GetProperties());
            foreach (PropertyInfo pi in Properties)
            {
                if (HasAttribute(pi, attrType, true))
                {
                    if (propValue != null && pi.PropertyType != propValue.GetType())
                    {
                        pi.SetValue(instance, Convert.ChangeType(propValue, pi.PropertyType), null);
                    }
                    else
                    {
                        pi.SetValue(instance, propValue, null);
                    }
                    break;
                }
            }
        }

        /// <summary>
        /// 获取映射实例的连接库连接对象
        /// </summary>
        /// <param name="sqlTableEntry">对象映射实例</param>
        /// <returns>抽象的数据库连接</returns>
        public static DbConnection GetDbConnection(this TableEntry sqlTableEntry)
        {
            return new AdoNetAdapter(sqlTableEntry).GetDbConnection();
        }


        /// <summary>
        /// 获取实例的指定属性类型的属性的值
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="attrType">特性类型</param>
        /// <returns></returns>
        public static object GetByAttribute(this object instance, Type attrType)
        {
            if (instance == null)
            {
                throw new NullReferenceException("相关实例不存在！");
            }

            Type instanceType = instance.GetType();
            object objRet = null;
            PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(instanceType.FullName, () => instanceType.GetProperties());
            foreach (PropertyInfo pi in Properties)
            {
                if (HasAttribute(pi, attrType, true))
                {
                    objRet = pi.GetGetMethod().Invoke(instance, null);
                    break;
                }
            }
            return objRet;
        }

        /// <summary>
        /// 获取相关实例的词典键值对表示形式
        /// </summary>
        /// <param name="instance">需要转换的实例</param>
        /// <param name="instanceType">实例类型</param>
        /// <returns></returns>
        public static DictionaryEntry[] ToDictionaryEntryArray(this object instance, Type instanceType)
        {
            return ToDictionaryEntryArray(instance, instanceType, null);
        }

        /// <summary>
        /// 获取相关实例的词典键值对表示形式
        /// </summary>
        /// <param name="instance">需要转换的实例</param>
        /// <param name="instanceType">实例类型</param>
        /// <param name="fieldNames">字段集合</param>
        /// <param name="isIgnore">是否是忽略字段,否则为包含字段.</param>
        /// <returns></returns>
        public static DictionaryEntry[] ToDictionaryEntryArray(this object instance, Type instanceType,
            string[] fieldNames, bool isIgnore)
        {
            List<DictionaryEntry> entryList = new List<DictionaryEntry>();
            string propName = "";
            PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(instanceType.FullName, () => instanceType.GetProperties());
            foreach (PropertyInfo pi in Properties)
            {
                propName = pi.Name;
                if (fieldNames != null && string.Join("", fieldNames).Trim() != string.Empty)
                {
                    //if (instanceType.IsSubclassOf(typeof(TableEntry)))
                    //{
                    //    //标记有映射转换
                    //    TableItemAttribute[] itemAttr = GetMemberInfoCustomAttributes<TableItemAttribute>(pi, typeof(TableItemAttribute), true);
                    //    if (itemAttr.Length > 0)
                    //    {
                    //        propName = itemAttr[0].BindField;
                    //    }
                    //}

                    if (isIgnore == true)
                    {
                        if (!StringArrayContains(fieldNames, propName))
                        {
                            entryList.Add(new DictionaryEntry(pi.Name, pi.GetGetMethod().Invoke(instance, null)));
                        }
                    }
                    else
                    {
                        if (StringArrayContains(fieldNames, propName))
                        {
                            entryList.Add(new DictionaryEntry(pi.Name, pi.GetGetMethod().Invoke(instance, null)));
                        }
                    }
                }
                else
                {
                    entryList.Add(new DictionaryEntry(pi.Name, pi.GetGetMethod().Invoke(instance, null)));
                }
            }
            return entryList.ToArray();
        }

        /// <summary>
        /// 获取相关实例的词典键值对表示形式
        /// </summary>
        /// <param name="instance">需要转换的实例</param>
        /// <param name="instanceType">实例类型</param>
        /// <param name="ignoreNames">需要忽略的属性名称集合</param>
        /// <returns></returns>
        public static DictionaryEntry[] ToDictionaryEntryArray(this object instance, Type instanceType,
            string[] ignoreNames)
        {
            return ToDictionaryEntryArray(instance, instanceType, ignoreNames, true);
        }
        #endregion


        #region ORM工具
        //相关实例映射表是否已创建
        internal static bool TableInitialized = false;

        
        /// <summary>
        /// 初始化所有映射实例（自动建表)
        /// </summary>
        public static void InitializeTables()
        {
            InitializeTables(null);
        }

        /// <summary>
        /// 带过滤条件初始化映射实例（自动建表)
        /// </summary>
        /// <param name="tabMatch">表定义的过滤适配</param>
        public static void InitializeTables(Predicate<SqlTable> tabMatch)
        {
            if (TableInitialized == true) return;

            //获取所有表映射实例
            //建立外键依赖表，建立主键关联表

            //表名，优先级：数值越大优先级越高
            Dictionary<string, int> TableBuldPriorityDict  = new  Dictionary<string,int>();

            //表名，映射实例类型
            Dictionary<string, TableEntry> LateCreateDict = new Dictionary<string, TableEntry>();

            string key = null;
            foreach(Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                #region 跳过全局缓存程序集和微软公司的程序集
                if (asm.GlobalAssemblyCache) continue;
                object[] asmCom = asm.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
                if (asmCom != null && asmCom.Length > 0)
                {
                    AssemblyCompanyAttribute asmCompany = (AssemblyCompanyAttribute)asmCom[0];
                    if (asmCompany.Company.Equals("Microsoft Corporation", StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;
                    }
                }
                #endregion

                foreach (Type currentType in asm.GetTypes())
                {
                    if (!currentType.IsAbstract && currentType.IsSubclassOf(typeof(TableEntry)))
                    {
                        TableEntry entry = Activator.CreateInstance(currentType) as TableEntry;
                        //没有设置绑定表映射
                        if (!HasAttribute(currentType, typeof(BindTableAttribute), true)) continue;

                        SqlTable tableConfig = EntryTableCache.GetTableCache(entry);
                        //表定义过滤
                        if (tabMatch != null && !tabMatch(tableConfig)) continue;

                        #region 建表或延迟建表（根据优先级）
                        if (tableConfig.ForeignKeys.Length == 0)
                        {
                            AdoNetAdapter.CreateTable(entry);
                        }
                        else
                        {
                            foreach (ForeignKeyAttribute fKey in tableConfig.ForeignKeys)
                            {
                                key = fKey.ForeignKeyTable.ToLower();
                                if (TableBuldPriorityDict.ContainsKey(key))
                                {
                                    TableBuldPriorityDict[key] = TableBuldPriorityDict[key] + 1 ;
                                }
                                else
                                {
                                    TableBuldPriorityDict.Add(key, 1);
                                }
                            }

                            key = tableConfig.Name.ToLower();
                            if (!LateCreateDict.ContainsKey(key))
                            {
                                LateCreateDict.Add(key, entry);
                            }
                        }
                        #endregion
                    }
                }
            }

            #region 使用内存数据表排序
            System.Data.DataTable sortTab = new System.Data.DataTable();
            sortTab.Columns.Add(new System.Data.DataColumn("TabName", typeof(string)));
            sortTab.Columns.Add(new System.Data.DataColumn("Priority", typeof(int)));
            foreach(string pKey in TableBuldPriorityDict.Keys)
            {
                System.Data.DataRow dRow = sortTab.NewRow();
                dRow["TabName"] = pKey;
                dRow["Priority"] = TableBuldPriorityDict[pKey];
                sortTab.Rows.Add(dRow);
            }
            #endregion

            System.Data.DataRow[] rows = sortTab.Select(string.Empty, "Priority Desc");
            for (int i = 0; i < rows.Length; i++)
            {
                //Console.WriteLine("{0}:{1}", rows[i]["TabName"], rows[i]["Priority"]);
                key = rows[i]["TabName"].ToString().ToLower();
                if (LateCreateDict.ContainsKey(key))
                {
                    //build
                    AdoNetAdapter.CreateTable(LateCreateDict[key]);
                    LateCreateDict.Remove(key);
                }
            }
            sortTab.Dispose();

            if (LateCreateDict.Count > 0)
            {
                foreach (string LKey in LateCreateDict.Keys)
                {
                    AdoNetAdapter.CreateTable(LateCreateDict[LKey]);
                }
            }

            TableInitialized = true;
        }
        #endregion

    }

}